﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;

#if !PocketPC
using System.Windows;
using MyUtils.Performance.UI;
#endif

namespace MyUtils.Performance
{
	public class ProfilingHelperInstance
	{
		internal readonly Dictionary<string, TimeSpan> Info = new Dictionary<string, TimeSpan>();

//		readonly object _thCreatingLock = new object();
//		volatile Thread _th;

		readonly ProfilingNode _rootNode = ProfilingNode.CreateRoot();

		internal ProfilingHelperInstance()
		{
			lock (ProfilingHelper.Instances)
			{
				ProfilingHelper.Instances.Add(this);
			}
			StartAutoFlusher();
		}

		internal void StartAutoFlusher()
		{
//			return;
//			if (ProfilingHelper.Enabled)
//			{
//				if (_th == null)
//				{
//					lock (_thCreatingLock)
//					{
//						if (_th == null)
//						{
//							var th = new Thread(() =>
//								{
//									while (true)
//									{
//										Thread.Sleep(5000);
//										FlushLog();
//									}
//								}) {IsBackground = true, Name = "Profiling Results Publisher"};
//							th.Start();
//							_th = th;
//						}
//					}
//				}
//			}
		}

		public void FlushLog()
		{
			if (_dirty)
			{
				_dirty = false;
				Trace.WriteLine(GetLog());
			}
		}

		private bool _dirty;

		internal bool Dirty
		{
			get { return _dirty; }
			set { _dirty = value; }
		}

		public void Clear()
		{
			lock (Info)
			{
				Info.Clear();
			}
		}

		readonly Dictionary<Thread, Stack<ProfilingNode>> _threadStack = new Dictionary<Thread, Stack<ProfilingNode>>();

		internal Stack<ProfilingNode> CurrentStack
		{
			get
			{
				Stack<ProfilingNode> stack;
				var key = Thread.CurrentThread;
				//lock (_threadStack)
				{
					if (!_threadStack.TryGetValue(key, out stack))
					{
						_threadStack[key] = stack = new Stack<ProfilingNode>(new[]
						{
							_rootNode.SubNode(string.Format("Thread: {0} {1}", key.ManagedThreadId, key.Name)),
						});
					}
				}
				return stack;
			}
		}

		public IDisposable Section(string sectionName)
		{
			if (ProfilingHelper.Enabled)
			{
				var parent = CurrentStack.Peek();
				var node = parent.SubNode(sectionName);
				CurrentStack.Push(node);
				return new ProfilingSection(this, sectionName, node);
			}
			return null;
		}

		public override string ToString()
		{
			return GetLog();
		}

#if !PocketPC
		public void ShowLogModal()
		{
			var th = new Thread(ShowLogModalWorker);
			th.SetApartmentState(ApartmentState.STA);
			th.Start();
			th.Join();
		}

		void ShowLogModalWorker()
		{
			var app = new Application();
			var analyzer = new PerfLogAnalyzer { DataContext = _rootNode };
			app.Run(analyzer);
			analyzer.ClosedEv.WaitOne();
			app.Shutdown();
			return;
		}
#endif

		public string GetLog()
		{
			var sb = new StringBuilder();
			sb.AppendLine();
			sb.AppendLine("<ProfilingLog>");
			lock (Info)
			{
				foreach (var kvp in Info.OrderByDescending(k => k.Value))
				{
					sb.AppendFormat(" \t{0} \t: {1}\r\n", kvp.Key, kvp.Value);
				}
			}
			sb.AppendLine("</ProfilingLog>");

			sb.AppendLine();
			var doc = new XmlDocument();
			_rootNode.FillLog(doc, doc);

			using (var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true, IndentChars = " " }))
			{
				doc.WriteTo(writer);
			}

			return sb.ToString();
		}
	}
}